
// import * as d3 from "../lib/d3.js";
import * as d3 from "d3";
import {$} from "../util/seq.js";
import {handleAsChord} from "./keyboard.js";
import {firstFocusable, nextFocusable, lastFocusable} from "./focusable.js";

function focusOn(node) {
    node?.focus();
    return true;
}

/**
 * @param elements d3 selection of elements that share keyboard focus navigation
 * @param {Object?} options enableUpDown: boolean
 */
export function defineFocusNav(elements, options = {}) {
    const {enableUpDown = false} = options;
    const nodes = elements.nodes();

    elements.on($`keydown`, handleAsChord(function(chord) {
        const i = nodes.indexOf(this);
        const dir = d3.select(this).style("direction") === "rtl" ? -1 : 1;
        switch (chord) {
            case "Home":       return focusOn(firstFocusable(nodes));
            case "ArrowLeft":  return focusOn(nextFocusable(nodes, i, -dir));
            case "ArrowRight": return focusOn(nextFocusable(nodes, i, dir));
            case "ArrowUp":    return enableUpDown && focusOn(nextFocusable(nodes, i, -1));
            case "ArrowDown":  return enableUpDown && focusOn(nextFocusable(nodes, i, 1));
            case "End":        return focusOn(lastFocusable(nodes));
        }
    }));
}

/**
 * @param elements d3 selection of elements that share keyboard focus navigation on a grid
 * @param {number} width the grid's row size
 */
export function defineGridFocusNav(elements, width) {
    const nodes = elements.nodes();

    function row(i) {
        return Math.floor(i / width);
    }

    elements.on($`keydown`, handleAsChord(function(chord) {
        const i = nodes.indexOf(this);
        const dir = d3.select(this).style("direction") === "rtl" ? -1 : 1;
        switch (chord) {
            case "Control-Home":  return focusOn(firstFocusable(nodes));
            case "ArrowLeft":     return focusOn(nextFocusable(nodes, i, -dir));
            case "ArrowRight":    return focusOn(nextFocusable(nodes, i, dir));
            case "ArrowUp":       return focusOn(nextFocusable(nodes, i, -width));
            case "ArrowDown":     return focusOn(nextFocusable(nodes, i, width));
            case "Home":          return focusOn(nextFocusable(nodes, row(i) * width - 1, 1));
            case "End":           return focusOn(nextFocusable(nodes, (row(i) + 1) * width, -1));
            case "Control-End":   return focusOn(lastFocusable(nodes));
        }
    }));
}
